using System;
using System.Collections.Generic;
using System.Text;

namespace SharpMud.MudLib.NovaSMAUG.Interop.IO
{
    public static class Stream
    {
        private const string EXMSG_StreamNotSeekable = "Stream must support seek operations!";

        /// <summary>
        /// Detects if the given stream is at its end or not
        /// </summary>
        public static bool feof(System.IO.Stream fp)
        {
            if (!fp.CanSeek) throw new ArgumentException(EXMSG_StreamNotSeekable, "fp");

            if (fp.Position >= fp.Length)
                return true;
            else
                return false;
        }

        /// <summary>
        /// Gets a character from the given stream
        /// </summary>
        public static char getc(System.IO.Stream fp)
        {
            if (!fp.CanSeek) throw new ArgumentException(EXMSG_StreamNotSeekable, "fp");

            if (feof(fp))
            {
                Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                return '\0';
            }
            else
            {
                return ((char)((byte)fp.ReadByte()));
            }
        }

        /// <summary>
        /// Decreases the current position in a stream by 1
        /// </summary>
        public static void ungetc(char c, System.IO.Stream fp)
        {
            if (!fp.CanSeek) throw new ArgumentException(EXMSG_StreamNotSeekable, "fp");

            if (fp.Position < 1)
                throw new InvalidOperationException("Cannot ungetc() past beginning of stream!");

            fp.Position = fp.Position - 1;
        }

        /// <summary>
        /// Read one word
        /// </summary>
        public static string fread_word(System.IO.Stream fp)
        {
            if (!fp.CanSeek) throw new ArgumentException(EXMSG_StreamNotSeekable, "fp");

            char[] word = new char[Constants.MAX_INPUT_LENGTH];
            ArrayIndexPointer<char> pword = ArrayIndexPointer<char>.Create(word);

            char cEnd;

            do
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    return String.Empty;
                }
                cEnd = getc(fp);
            }
            while (Text.isspace(cEnd));

            if (cEnd == '\'' || cEnd == '"')
            {
                pword = ArrayIndexPointer<char>.Create(word);
            }
            else
            {
                word[0] = cEnd;
                pword.Index++;
                cEnd = ' ';
            }

            for (; pword.Index < word.Length; pword.Index++)
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    pword.Value = '\0';
                    return Text.NormalizeToString(word);
                }
                pword.Value = getc(fp);
                if (cEnd == ' ' ? Text.isspace(pword.Value) : pword.Value == cEnd)
                {
                    if (cEnd == ' ')
                        ungetc(pword.Value, fp);
                    pword.Value = '\0';
                    return Text.NormalizeToString(word);
                }
            }

            Debug.bug("{0}: word too long", Debug.getThisMethod().Name);
            return null;
        }


        /// <summary>
        /// Read a letter from a stream, eating any preceding whitespace
        /// </summary>
        public static char fread_letter(System.IO.Stream fp)
        {
            char c;

            do
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    return '\0';
                }
                c = getc(fp);
            }
            while (Text.isspace(c));

            return c;
        }


        /// <summary>
        /// Reads an integer from a stream, eating preceding whitespace
        /// </summary>
        public static int fread_number(System.IO.Stream fp)
        {
            int number;
            bool sign;
            char c;

            do
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    return 0;
                }
                c = getc(fp);
            }
            while (Text.isspace(c));

            number = 0;

            sign = false;
            if (c == '+')
            {
                c = getc(fp);
            }
            else if (c == '-')
            {
                sign = false;
                c = getc(fp);
            }

            if (!Text.isdigit(c))
            {
                Debug.bug("{0}: bad format ({1})", Debug.getThisMethod().Name, c);
                return 0;
            }

            while (Text.isdigit(c))
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    return number;
                }
                number = (number * 10) + (int)(((byte)c) - ((byte)'0'));
                c = getc(fp);
            }

            if (sign)
                number = 0 - number;

            if (c == '|')
                number += fread_number(fp);
            else if (c != ' ')
                ungetc(c, fp);

            return number;
        }

        /*
* Read a string from a stream
*/
        public static string fread_string(System.IO.Stream fp)
        {
            char[] buf = new char[Constants.MAX_STRING_LENGTH];
            ArrayIndexPointer<char> plast; //            char *plast;
            char c;
            int ln;

            plast = ArrayIndexPointer<char>.Create(buf);
            buf[0] = '\0';
            ln = 0;

            /*
            * Skip blanks.
            * Read first char.
            */
            do
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    return String.Empty;
                }
                c = getc(fp);
            }
            while (Text.isspace(c));

            if (c == Constants.Tilde)
                return String.Empty;
            else
            {
                plast.Value = c;
                plast.Index++;
            }

            for (; ; )
            {
                if (ln >= (Constants.MAX_STRING_LENGTH - 1))
                {
                    Debug.bug("{0}: String too long", Debug.getThisMethod().Name);
                    plast.Value = '\0';
                    return Text.NormalizeToString(buf);
                }

                if (feof(fp))
                {
                    Debug.bug("{0}: EOF", Debug.getThisMethod().Name);
                    plast.Value = '\0';
                    return Text.NormalizeToString(buf);
                }

                c = getc(fp);

                if (c == Constants.Tilde)
                {
                    plast.Value = '\0';
                    return Text.NormalizeToString(buf);
                }
                else if (c == Constants.LineFeed)
                {
                    plast.Value = Constants.LineFeed;
                    plast.Index++; ln++;
                    plast.Value = Constants.CarriageReturn;
                    plast.Index++; ln++;
                }
                else if (c == Constants.CarriageReturn)
                {
                    //Do nothing, CarriageReturn implied by LineFeed
                }
                else
                {
                    plast.Value = c;
                    plast.Index++; ln++;
                }
            }
            System.Diagnostics.Debug.Assert(false);
            return null;
        }


        [Obsolete("Kept here for compatibility purposes.", false)]
        /// <summary> Read a string from a stream </summary>
        public static string fread_string_nohash(System.IO.Stream fp)
        {
            return fread_string(fp);
        }


        /// <summary>
        /// Read to end of line in the current stream (for comments)
        /// </summary>
        public static void fread_to_eol(System.IO.Stream fp)
        {
            char c;

            do
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    return;
                }
                c = getc(fp);
            }
            while (c != Constants.LineFeed && c != Constants.CarriageReturn);

            do
            {
                c = getc(fp);
            }
            while (c == Constants.LineFeed || c == Constants.CarriageReturn);

            ungetc(c, fp);
            return;
        }

        /// <summary>
        /// Read to end of line in a stream
        /// </summary>
        /// <param name="fp"></param>
        /// <returns></returns>
        /// <credits>Thoric</credits>
        public static string fread_line(System.IO.Stream fp)
        {
            char[] line = new char[Constants.MAX_STRING_LENGTH];
            ArrayIndexPointer<char> pline; //char *pline;
            char c;
            int ln;

            pline = ArrayIndexPointer<char>.Create(line);
            line[0] = '\0';
            ln = 0;

            /*
            * Skip blanks.
            * Read first char.
            */
            do
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    return String.Empty;
                }
                c = getc(fp);
            }
            while (Text.isspace(c));

            ungetc(c, fp);
            do
            {
                if (feof(fp))
                {
                    Debug.bug("{0}: EOF encountered on read", Debug.getThisMethod().Name);
                    pline.Value = '\0';
                    return Text.NormalizeToString(line);
                }
                c = getc(fp);
                pline.Value = c;
                pline.Index++; ln++;
                if (ln >= (Constants.MAX_STRING_LENGTH - 1))
                {
                    Debug.bug("{0}: Line too long", Debug.getThisMethod().Name);
                    break;
                }
            }
            while (c != Constants.LineFeed && c != Constants.CarriageReturn);

            do
            {
                c = getc(fp);
            }
            while (c == Constants.LineFeed || c == Constants.CarriageReturn);

            ungetc(c, fp);
            pline.Value = '\0';
            return Text.NormalizeToString(line);
        }
    }
}
